from checkers_board import *
from checkers_control import Controller
from checkers_config import *
from checkers_config import CheckersGameMode as Mode
import Tkinter as tk
from Tkinter import *
from Tkconstants import W, E
from time import sleep
import tkMessageBox

class CheckersUI(object):
    def __init__(self, root, is_graphical=False, game_engine=None):
        self._gui_mode = is_graphical
        if game_engine is not None:
            self._engine = game_engine
       
        root.resizable(False, False)
        self._game_mode = Mode.HUMAN_VERSUS_AI
        self.root = root
        self.root.title('ai-checkers')
        
        # mip = is move in progress
        self._mip = False
        self._highlights = []
        self._enabled_squares = []
        self._chosen_piece = None
        
        self._square_size = DEFAULT_SIZE / 8
        self.light_color = LIGHT_CHECKERS
        self.dark_color = DARK_CHECKERS
        self._piece_offset = self._square_size / 5
        self._boardpos = create_position_map()
        self._gridpos = create_grid_map()
        self._white_crownpic = PhotoImage(file=os.path.join(RESOURCES, 'crown.gif'))
        self._black_crownpic = PhotoImage(file=os.path.join(RESOURCES, 'crown-inverted.gif'))
        self._crownpics = {KBLACK: self._black_crownpic, KWHITE: self._white_crownpic}
        
        # Setup menu
        self._setup_menu()
        
        # Prepare canvas for board
        self.canvas_frame = Frame(root)
        self.canvas_frame.pack(side="bottom", fill="both", expand="true")
        self.canvas = Canvas(self.canvas_frame, width=DEFAULT_SIZE,
                             height=DEFAULT_SIZE, borderwidth=0,
                             highlightthickness=0)
        self.canvas.pack(side="bottom", fill="both", expand="true")
        
        self.root.after_idle(self.center_on_screen)
        self.root.update()
        self.root.deiconify()
        self.start_new_game()
        
    def on_settings_change(self):
        self._engine.set_heuristic_function(self.heuristic_chosen.get())
        self._engine.set_max_depth(self.depth_chosen.get())
        self._engine.set_algorithm(self.algorithm_chosen.get())

    def change_game_mode(self):
        if self._game_mode == Mode.HUMAN_VERSUS_AI:
            self.remove_highlights()
            self._unregister_event_handlers()
            self._game_mode = Mode.AI_VERSUS_AI
            self._engine._ai_color = [CheckersTurns.WHITE, CheckersTurns.BLACK]
            self.white_player_button['text'] = 'Set white as human'
        elif self._game_mode == Mode.AI_VERSUS_AI:
            self._game_mode = Mode.HUMAN_VERSUS_AI
            self._engine._ai_color = CheckersTurns.BLACK
            self.white_player_button['text'] = 'Set white as AI'
        else:
            raise ValueError('Unknown game mode')
        self._engine._game_mode = self._game_mode
        self._action_loop()
            
    def start_new_game(self):
        self._engine = Controller(self._game_mode)
        self.info_label['text'] = INFO_TEMPLATE % (self._engine.get_current_turn(), )
        self._setup_board()
        self._draw_checkers()
        self._action_loop()
        
    def _action_loop(self):
        game_result = self._engine.is_game_over()
        if game_result:
            tkMessageBox.showinfo("Game finished!", CheckersEnd.to_string(game_result), parent=self.root)
            return
            
        # before_turn = self._engine.get_opponent_pieces()
        self.root.update()
        if self._engine._game_mode == Mode.HUMAN_VERSUS_AI:
            if self._engine._current_turn == self._engine._ai_color:
                self._AI_play()
            else:
                self._human_play()
        elif self._engine._game_mode == Mode.AI_VERSUS_AI:
            self._AI_play()
        # after_turn = self._engine.get_opponent_pieces()
        # if after_turn != before_turn:
            # self._engine._no_eat_streak = 0
                
    def _AI_play(self):
        self._engine.AI_play()
        self.draw_logic_board()
        self.canvas.after(100, self._turn_finished)
        
    def _human_play(self):
        jumpable_pieces = self._engine._get_jumpable_pieces()
        if jumpable_pieces:
            self.enable_pieces(jumpable_pieces)
        else:
            self.enable_pieces(self._engine._get_movable_pieces())
        
    def _end_white_turn(self):
        pass
        
    def _end_black_turn(self):
        pass
        
    def _turn_finished(self):
        if self._engine._current_turn == CheckersTurns.WHITE:
            self.remove_highlights()
            self._unregister_event_handlers()
            self._engine._current_turn = CheckersTurns.BLACK
            self._end_white_turn()
        else:
            self._engine._current_turn = CheckersTurns.WHITE
            self._end_black_turn()
            
        self.info_label['text'] = INFO_TEMPLATE % (self._engine.get_current_turn(), )
        self.root.update()
        self._action_loop()
        
    def _mouse_click(self, event):
        xi, yi, sq = self.calc_board_coords(event.x, event.y)
        if sq == 0 and ((xi, yi) != (7, 0)) and not self._mip:
            return
        if sq not in self._enabled_squares:
            if not self._mip:
                return
            else:
                self._mip = False
                self._chosen_piece = None
                self.remove_highlights()
                self._enabled_squares = []
                self._human_play()
                return
                
        if not self._mip:
            self._mip = True
            self._chosen_piece = sq
            self.remove_highlights()
            self.highlight_square(sq, OUTLINE_COLOR)
            self._highlights.append(sq)
            available_locs = self._engine._get_new_locations_for_human_piece(2 ** sq)
            self._enabled_squares = get_set_indexes(available_locs)
            self.add_highlights(self._enabled_squares, 'blue')
        else:
            self._enabled_squares = []
            self._engine._move_location_for_human_piece(2 ** self._chosen_piece, 2 ** sq)
            self.draw_logic_board()
            self.remove_highlights()
            self._mip = False
            self._chosen_piece = None
            self.canvas.after(100, self._turn_finished)
        
    def highlight_square(self, idx, color):
        row, col = self._gridpos[idx]
        hpos = col + row * 8
        self.canvas.itemconfigure('o'+str(hpos), outline=color)    
    
    def add_highlights(self, sq_indexes, color):
        happ = self._highlights.append
        for i in sq_indexes:
            self.highlight_square(i, color)
            happ(i)
    
    def remove_highlights(self):
        for h in self._highlights:
            self.highlight_square(h, DARK_SQUARES)
        self._highlights = []
            
    def enable_pieces(self, pieces_descriptor):
        pieces_descriptor = c_ulong(pieces_descriptor)
        if not self._gui_mode:
            print '> Movable pieces: %s' % (get_set_indexes(pieces_descriptor.value), )
            chosen_piece = int(raw_input('> Please choose a piece to move: '))
            return 2 ** chosen_piece
        else:
            self._enabled_squares = get_set_indexes(pieces_descriptor.value)
            self.add_highlights(self._enabled_squares, OUTLINE_COLOR)
            self._register_event_handlers()

    def _setup_board(self):
        for r in range(0, 8, 2):
            row = r * self._square_size
            for c in range(0, 8, 2):
                col = c * self._square_size
                self.canvas.create_rectangle(col, row, col+self._square_size-1,
                                             row+self._square_size-1,
                                             fill=LIGHT_SQUARES,
                                             outline=LIGHT_SQUARES)
                self.canvas.create_rectangle(col, row+self._square_size,
                                             col+self._square_size-1,
                                             row+self._square_size*2-1,
                                             fill=DARK_SQUARES,
                                             outline=DARK_SQUARES,
                                             tags='o'+str(((r+1)*8)+c),
                                             width=2.0)
            for c in range(1, 8, 2):
                col = c * self._square_size
                self.canvas.create_rectangle(col, row+self._square_size,
                                             col+self._square_size-1,
                                             row+self._square_size*2-1,
                                             fill=LIGHT_SQUARES,
                                             outline=LIGHT_SQUARES)
                self.canvas.create_rectangle(col, row, col+self._square_size-1,
                                             row+self._square_size-1,
                                             fill=DARK_SQUARES,
                                             outline=DARK_SQUARES,
                                             tags='o'+str(r*8+c),
                                             width=2.0)
                                             
    def _draw_checkers(self, add_dict={}, del_dict={}):
        if add_dict == {} and del_dict == {}:
            add_dict = g_pdict
            del_dict = g_pdict
            
        for i, checker in del_dict.iteritems():
            try:
                self.canvas.delete('c'+str(i))
            except:
                continue
        for i, checker in add_dict.iteritems():
            if checker == FREE:
                continue
            color = 'black' if (checker in (PBLACK, KBLACK)) else self.light_color
            row, col = self._gridpos[i]
            x = col * self._square_size + self._piece_offset
            y = row * self._square_size + self._piece_offset
            tag = 'c' + str(i)
            self.canvas.create_oval(x + 2, y + 2, x + 2 + CHECKER_SIZE, y+2+CHECKER_SIZE, outline='black', fill='black', tags=(color, tag))
            self.canvas.create_oval(x, y, x + CHECKER_SIZE, y + CHECKER_SIZE, outline='black', fill=color, tags=(color, tag))
            if checker in (KWHITE, KBLACK):
                self.canvas.create_image(x + 15, y + 15, image=self._crownpics[checker], anchor=CENTER, tags=(color, tag))
                 

            
    def center_on_screen(self):
        self.root.update_idletasks()
        sw = self.root.winfo_screenwidth()
        sh = self.root.winfo_screenheight()
        w = self.root.winfo_reqwidth()
        h = self.root.winfo_reqheight()
        new_geometry = "+%d+%d" % ((sw-w)/2, (sh-h)/2)
        self.root.geometry(newGeometry=new_geometry)
        
    def _register_event_handlers(self):
        Widget.bind(self.canvas, '<Button-1>', self._mouse_click)
     
    def _unregister_event_handlers(self):
        Widget.unbind(self.canvas, '<Button-1>')
    
    def calc_board_coords(self, ex, ey):
        vx = min(max(0, self.canvas.canvasx(ex)), DEFAULT_SIZE-1)
        vy = min(max(0, self.canvas.canvasy(ey)), DEFAULT_SIZE-1)
        xi = int(vx / self._square_size)
        yi = int(vy / self._square_size)
        pos = self._boardpos.get(xi + yi * 8, 0)
        return yi, xi, pos
        
    def draw_logic_board(self):
        whites = self._engine._logic_board.cwhites.value
        blacks = self._engine._logic_board.cblacks.value
        empty = flipnum(whites | blacks)
        
        white_list = get_set_indexes(whites)
        black_list = get_set_indexes(blacks)
        blanks = get_set_indexes(empty)
        kings = get_set_indexes(self._engine._logic_board.ckings.value)
        
        add_dict = {}
        del_dict = {}
        for white in white_list:
            if white in kings:
                add_dict[white] = KWHITE
            else:
                add_dict[white] = PWHITE
        for black in black_list:
            if black in kings:
                add_dict[black] = KBLACK
            else:
                add_dict[black] = PBLACK
        for blank in blanks:
            del_dict[blank] = FREE
            
        self._draw_checkers(add_dict=add_dict, del_dict=del_dict)
        
        
    def _setup_menu(self):
        # Menu
        self.menu_frame = Frame(self.root)
        self.menu_frame.pack(side="top", fill="both", expand="true")
        self.menu_frame['bg'] = 'white'

        self.top_menu = Frame(self.menu_frame)
        self.top_menu.pack(side="top", fill="both", expand="true")
        self.top_menu['bg'] = 'white'
        
        # Setting text
        self.general_label = Label(self.top_menu, text='Checkers AI', font=("Helvetica", 15))
        self.general_label['bg'] = 'white'
        self.general_label.pack(side='top', pady=0)
        
        # Swap white player
        self.white_player_button = Button(self.top_menu, text='Set white as AI', command=self.change_game_mode)
        self.white_player_button.pack(side='left', pady=15, padx = 20)
        
        # New game button
        self.new_game_button = Button(self.top_menu, text='Restart', font=("Helvetica", 13), command=self.start_new_game)
        self.new_game_button.pack(side='left', pady=15, padx = 30)

        # OptionMenu choices
        self.algorithm_chosen = tk.StringVar(self.root)
        self.algorithm_chosen.set(DEFAULT_ALGORITHM)
        self.depth_chosen = tk.IntVar(self.root)
        self.depth_chosen.set(DEFAULT_SEARCH_DEPTH)
        self.heuristic_chosen = tk.IntVar(self.root)
        self.heuristic_chosen.set(DEFAULT_HEURISTIC)
        
        # Setting text
        self.settings_label = Label(self.menu_frame, text='Settings', font=("Helvetica", 15))
        self.settings_label['bg'] = 'white'
        self.settings_label.pack(side='top', pady=0)
        
        # Algorithm choice
        self.algorithm_menu = Frame(self.menu_frame)
        self.algorithm_menu.pack(side="left", fill="both", expand="true")
        self.algorithm_menu['bg'] = 'white'
        
        self.algorithm_label = Label(self.algorithm_menu, text=ALGORITHM_LABEL)
        self.algorithm_label['bg'] = 'white'
        self.algorithm_label.pack(side='top')
        
        choices = ['alpha-beta', 'minimax', ]
        option = tk.OptionMenu(self.algorithm_menu, self.algorithm_chosen, *choices)
        option.pack(side='bottom', padx=5, pady=5)
        
        # Search depth choice
        self.search_depth_menu = Frame(self.menu_frame)
        self.search_depth_menu.pack(side="left", fill="both", expand="true", padx = 10, pady = 0)
        self.search_depth_menu['bg'] = 'white'
        
        self.search_depth_label = Label(self.search_depth_menu, text=SEARCH_DEPTH_LABEL)
        self.search_depth_label['bg'] = 'white'
        self.search_depth_label.pack(side='top')
        
        choices = range(1, 16)
        option = tk.OptionMenu(self.search_depth_menu, self.depth_chosen, *choices)
        option.pack(side='bottom', padx=5, pady=5)

        # Heuristic function choice
        self.heuristic_menu = Frame(self.menu_frame)
        self.heuristic_menu.pack(side="left", fill="both", expand="true")
        self.heuristic_menu['bg'] = 'white'
        
        self.heuristic_func_label = Label(self.heuristic_menu, text=HEURISTIC_FUNCTION_LABEL)
        self.heuristic_func_label['bg'] = 'white'
        self.heuristic_func_label.pack(side='top')
        
        choices = range(1, 4)
        option = tk.OptionMenu(self.heuristic_menu, self.heuristic_chosen, *choices)
        option.pack(side='bottom', padx=5, pady=5)
        
        # Current turn
        self.info_label = Label(self.top_menu, text=INFO_TEMPLATE % ('Undecided', ))
        self.info_label['bg'] = 'white'
        self.info_label.pack(side='top', pady=20)
        
        # Apply
        button = tk.Button(self.menu_frame, text="Apply", command=self.on_settings_change)
        button.pack(side='right', padx=20, pady=0)
        